"use client" import * as React from 'react' import { useRouter } from 'next/navigation' import { Check, X, MinusCircle, ChevronRight, Target, Timer, Hash, Coins, Wrench, Play, Flag, Cpu, Plus, RotateCcw, Workflow, ShieldAlert, ShieldCheck, Inbox, ClipboardCheck } from 'lucide-react' import { Page, PageBody } from '@open-mercato/ui/backend/Page' import { Button } from '@open-mercato/ui/primitives/button' import { Popover, PopoverTrigger, PopoverContent } from '@open-mercato/ui/primitives/popover' import { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip' import { StatusBadge } from '@open-mercato/ui/primitives/status-badge' import { JsonDisplay } from '@open-mercato/ui/backend/JsonDisplay' import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail' import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall' import { flash } from '@open-mercato/ui/backend/FlashMessages' import { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent' import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation' import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context' import { useCoalescedReload } from '../../../components/useCoalescedReload' import { mapRunDetail, formatConfidence, formatDateTime, formatDurationMs, formatTokens, formatCostMinor, type ContextBundleView, type GuardrailCheckView, type ProposalView, type RunDetailView, type SpanView, type ToolCallView, } from '../../../components/types' import { deriveReasoning } from '../../../components/proposalFactsData' import { autoDispositionBlockMessageKey, proposalCaseStatus, proposalCaseStatusVariant, } from '../../../components/proposalCaseStatus' import { findProposalOption, normalizeProposalEnvelope, rankProposalOptions } from '../../../data/proposalEnvelope' import { buildSpanTimeline, truncateSpanName } from '../../../lib/trace/spanTimeline' import { runStatusVariant, runStatusLabelKey, confidenceFace, confidencePctOf, ConfidenceFaceValue } from '../../../components/cockpitStatus' import { EmptyArt } from '../../../components/EmptyArt' import { isAgentPreviewUiEnabled } from '../../../lib/featureFlags' // llm / tool / system get distinct DS tokens so the waterfall reads at a glance // without inventing colors (brand-violet / accent-indigo / muted neutral). const SPAN_KIND_BAR: Record = { llm: 'bg-brand-violet', tool: 'bg-accent-indigo', system: 'bg-muted-foreground', } function spanBarClass(kind: string): string { return SPAN_KIND_BAR[kind] ?? 'bg-muted-foreground' } function hasSummary(value: unknown): boolean { if (value == null) return false if (typeof value === 'string') return value.trim().length > 0 if (Array.isArray(value)) return value.length > 0 if (typeof value === 'object') return Object.keys(value as Record).length > 0 return true } function formatSummary(value: unknown): string { if (typeof value === 'string') return value try { return JSON.stringify(value, null, 2) } catch { return String(value) } } function parseTime(value: string | null): number | null { if (!value) return null const parsed = Date.parse(value) return Number.isFinite(parsed) ? parsed : null } function StatCell({ label, value, icon: Icon, iconClassName, }: { label: string value: string icon: React.ComponentType<{ className?: string }> iconClassName?: string }) { return (

{label}

{value}

) } function InspectorCard({ title, hint, sampleLabel, children, }: { title: string hint?: string sampleLabel?: string children: React.ReactNode }) { return (

{title}

{sampleLabel ? ( {sampleLabel} ) : null}
{hint ? {hint} : null}
{children}
) } // Context assembled (TDCR) — the run's persisted AgentContextBundle: routed // sources as chips, the "+N pruned" chip expands to reveal what was dropped, so // the routed-vs-pruned decision is inspectable. function contextSourceLabel(source: { ref: string; locator: string | null }): string { return source.locator ?? source.ref } function ContextAssembledCard({ bundle }: { bundle: ContextBundleView | null }) { const t = useT() const [showPruned, setShowPruned] = React.useState(false) if (!bundle) { return (

{t('agent_orchestrator.traces.detail.contextEmpty')}

) } const routed = bundle.routedSources const pruned = bundle.prunedSources return (

{t('agent_orchestrator.traces.detail.contextSummary', undefined, { routed: routed.length, total: routed.length + pruned.length, used: formatTokens(bundle.tokensUsed) ?? '—', budget: formatTokens(bundle.tokenBudget) ?? '—', })}

{routed.map((source, index) => ( {contextSourceLabel(source)} ))} {showPruned ? pruned.map((source, index) => ( {source.ref} )) : null} {pruned.length > 0 ? ( ) : null}
) } // Guardrails — the run's persisted AgentGuardrailCheck verdicts (all checks, // passed and tripped), each expandable to its redacted evidence + set version. const GUARDRAIL_RESULT_VARIANT: Record = { pass: 'success', warn: 'warning', block: 'error', } function GuardrailsCard({ checks }: { checks: GuardrailCheckView[] }) { const t = useT() const [openId, setOpenId] = React.useState(null) const passedCount = checks.filter((check) => check.result === 'pass').length return ( 0 ? t('agent_orchestrator.traces.detail.guardrailsHint', undefined, { passed: passedCount, total: checks.length, }) : undefined } > {checks.length === 0 ? (

{t('agent_orchestrator.traces.detail.noGuardrailChecks')}

) : (
    {checks.map((check) => { const open = openId === check.id const expandable = check.evidence != null || check.guardrailSetVersion != null return (
  • {open && expandable ? (
    {check.guardrailSetVersion ? (

    {t('agent_orchestrator.traces.detail.guardrailSetVersion')}:{' '} {check.guardrailSetVersion}

    ) : null} {check.evidence != null ? (

    {t('agent_orchestrator.traces.detail.guardrailEvidence')}

    ) : null}
    ) : null}
  • ) })}
)}
) } // Illustrative (Sample) model comparison — a confidence-bar leaderboard so the // speed/cost/quality trade-off reads at a glance instead of as a flat table. const MODEL_ALTERNATIVES = [ { model: 'claude-sonnet-4.x', confidence: 0.79, cost: 0.42 }, { model: 'gpt-4o', confidence: 0.83, cost: 1.1 }, ] function ModelComparisonCard({ currentModel }: { currentModel: string | null }) { const t = useT() const current = { model: currentModel ?? 'gpt-4o-mini', confidence: 0.86, cost: 2.05, current: true } const rows = [current, ...MODEL_ALTERNATIVES.map((row) => ({ ...row, current: false }))] const cheapest = rows.reduce((min, row) => (row.cost < min.cost ? row : min), rows[0]) const cheaperPct = Math.round((1 - cheapest.cost / current.cost) * 100) const confLowerPct = Math.round((1 - cheapest.confidence / current.confidence) * 100) const showVerdict = cheapest.model !== current.model && cheaperPct > 0 && confLowerPct >= 0 return (
{t('agent_orchestrator.traces.detail.confidence')} {t('agent_orchestrator.traces.detail.cost')}
    {rows.map((row) => (
  • {row.model} {row.current ? ( {t('agent_orchestrator.traces.detail.modelCurrent')} ) : null} {!row.current && row.model === cheapest.model ? ( {t('agent_orchestrator.traces.detail.modelCheapest')} ) : null} {row.cost.toFixed(2)} zł
  • ))}
{showVerdict ? (

{t('agent_orchestrator.traces.detail.modelVerdict', undefined, { model: cheapest.model, cost: cheaperPct, conf: confLowerPct, })}

) : null}
) } // Tool calls as an accordion — each row expands to its captured request and // response (both are real, redacted summaries from the trace). /** * On-demand loader for a full offloaded trace artifact (F1). When the row * carries an artifact key, the inline view is a redacted preview; this fetches * and renders the full decrypted payload through the secured artifact endpoint. * Renders nothing when there is no key (the payload was stored inline). */ function ArtifactExpander({ runId, artifactKey, kind, }: { runId: string artifactKey: string | null kind: 'output' | 'tool_request' | 'tool_response' }) { const t = useT() const [full, setFull] = React.useState(undefined) const [isLoading, setIsLoading] = React.useState(false) if (!artifactKey) return null const loadFull = async () => { setIsLoading(true) const call = await apiCall<{ payload: unknown }>( `/api/agent_orchestrator/runs/${encodeURIComponent(runId)}/artifact?key=${encodeURIComponent(artifactKey)}&kind=${kind}`, undefined, { fallback: { payload: undefined } }, ) setIsLoading(false) if (!call.ok) { flash(t('agent_orchestrator.traces.detail.artifactError'), 'error') return } setFull(call.result?.payload ?? null) } if (full !== undefined) { return (
) } return ( ) } /** Object/array summaries get the copy-affordance JSON view; strings keep the raw pre. */ function SummaryDisplay({ value }: { value: unknown }) { if (value != null && typeof value === 'object') { return } return
{formatSummary(value)}
} function ToolCallsCard({ toolCalls, runId }: { toolCalls: ToolCallView[]; runId: string }) { const t = useT() const [openId, setOpenId] = React.useState(toolCalls[0]?.id ?? null) return ( {toolCalls.length === 0 ? (

{t('agent_orchestrator.traces.detail.noToolCalls')}

) : (
{toolCalls.map((toolCall) => { const open = openId === toolCall.id return (
{open ? (
{toolCall.errorMessage ? (

{t('agent_orchestrator.traces.detail.toolError')}:{' '} {toolCall.errorMessage}

) : null} {hasSummary(toolCall.requestSummary) ? (

{t('agent_orchestrator.traces.detail.toolRequest')}

) : null} {hasSummary(toolCall.responseSummary) ? (

{t('agent_orchestrator.traces.detail.toolResponse')}

) : null}
) : null}
) })}
)}
) } // Real reasoning chain: the persisted rationale(s) of the run's proposal(s), // then rationales of nested upstream findings carried in the run input — the // same derivation the Caseload decision panel uses (`deriveReasoning`). /** * `output` is passed only so the card can drop what the Output JSON already * shows verbatim. Reasoning stays the readable home for the rationale; Output * stays the raw record. Neither should print the same sentence twice. */ function ReasoningCard({ proposals, input, output, }: { proposals: ProposalView[] input: unknown output: unknown }) { const t = useT() const renderedOutput = React.useMemo(() => { if (output == null) return '' try { return typeof output === 'string' ? output : JSON.stringify(output) } catch { return '' } }, [output]) const derived = React.useMemo( () => [ ...proposals.flatMap((proposal) => deriveReasoning(proposal.rationale, null)), ...deriveReasoning(null, input), ], [proposals, input], ) const items = React.useMemo( () => renderedOutput.length > 0 ? derived.filter((item) => !(item.text && renderedOutput.includes(item.text))) : derived, [derived, renderedOutput], ) // Nothing left BECAUSE Output already carries it is not the same as nothing // to say — the "no reasoning" empty state would be a lie, so drop the card. if (items.length === 0 && derived.length > 0) return null return ( 0 ? t('agent_orchestrator.traces.detail.reasoningHint', undefined, { count: items.length }) : undefined } > {items.length === 0 ? (

{t('agent_orchestrator.traces.detail.noReasoning')}

) : (
    {items.map((item, index) => (
  1. {index + 1} {item.label ? {item.label}: : null} {item.text}
  2. ))}
)}
) } /** * WHICH option the run's proposals were disposed with, and BY WHOM * (spec `2026-08-11-agent-taxonomy.md`, Phase 4 step 13). * * A trace that records only "approved" hides the decision: with N ranked * alternatives the interesting fact is which one ran and who chose it. * `disposition_by` is `rule:threshold` for an auto-approval and the acting * user's id otherwise — rendered as-is, because inventing a display name for an * id we cannot resolve would be worse than showing the id. */ function DispositionCard({ proposals }: { proposals: ProposalView[] }) { const t = useT() const locale = useLocale() if (proposals.length === 0) return null return (
    {proposals.map((proposal) => { const options = rankProposalOptions(normalizeProposalEnvelope(proposal.payload).options) const chosen = proposal.selectedOptionId ? findProposalOption(proposal.payload, proposal.selectedOptionId) : null const chosenRank = chosen ? options.findIndex((option) => option.id === chosen.id) + 1 : 0 const auto = proposal.dispositionBy === 'rule:threshold' const autoBlockMessageKey = autoDispositionBlockMessageKey(proposal.autoDispositionBlock) return (
  • {t(`agent_orchestrator.caseload.status.${proposalCaseStatus(proposal.disposition)}`)} {proposal.id.slice(0, 8)} {formatDateTime(proposal.updatedAt, locale) ?? ''}
    {t('agent_orchestrator.traces.detail.chosenOption')}
    {chosen ? ( {t('agent_orchestrator.proposal.options.rank', undefined, { rank: chosenRank })} · {chosen.label} ) : ( {options.length === 0 ? t('agent_orchestrator.proposal.options.noneProposed.title') : t('agent_orchestrator.traces.detail.noChosenOption')} )}
    {t('agent_orchestrator.traces.detail.disposedBy')}
    {proposal.dispositionBy == null ? ( {t('agent_orchestrator.traces.detail.notDisposed')} ) : auto ? ( t('agent_orchestrator.traces.detail.disposedByRule') ) : ( {proposal.dispositionBy} )}
    {options.length > 1 ? ( <>
    {t('agent_orchestrator.traces.detail.optionsOffered')}
    {options.length}
    ) : null}
    {autoBlockMessageKey ? (

    {t(autoBlockMessageKey)}

    ) : null}
  • ) })}
) } // File plane (#12): files the agent produced during the run (AgentRunArtifact), // fetched from the dedicated list endpoint and downloadable. Distinct from the // trace `ArtifactExpander` above (which loads offloaded tool-call payloads). type RunArtifactRow = { id: string fileName: string mimeType: string fileSize: number caption: string | null promotedAttachmentId: string | null createdAt: string } function formatArtifactSize(bytes: number): string { if (bytes < 1024) return `${bytes} B` if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` return `${(bytes / (1024 * 1024)).toFixed(1)} MB` } function FilesCard({ runId }: { runId: string }) { const t = useT() const locale = useLocale() const [rows, setRows] = React.useState(null) React.useEffect(() => { let active = true void (async () => { const call = await apiCall<{ items: RunArtifactRow[] }>( `/api/agent_orchestrator/runs/${encodeURIComponent(runId)}/artifacts`, undefined, { fallback: { items: [] } }, ) if (active && call.ok) setRows(call.result?.items ?? []) })() return () => { active = false } }, [runId]) // Hide the card entirely when the run produced no files (the common case). if (rows != null && rows.length === 0) return null return ( {rows == null ? (

{t('agent_orchestrator.artifacts.loading')}

) : (
    {rows.map((row) => (
  • {row.fileName} {row.promotedAttachmentId ? ( {t('agent_orchestrator.artifacts.promoted')} ) : null}
    {row.caption ? (

    {row.caption}

    ) : null}

    {formatArtifactSize(row.fileSize)} · {formatDateTime(row.createdAt, locale)}

    {t('agent_orchestrator.artifacts.download')}
  • ))}
)}
) } export default function AgentRunTracePage({ params }: { params?: { id?: string } }) { const t = useT() const locale = useLocale() const router = useRouter() const runId = params?.id ?? '' const [detail, setDetail] = React.useState(null) const [isLoading, setIsLoading] = React.useState(true) const [error, setError] = React.useState(null) const [reloadToken, setReloadToken] = React.useState(0) const loadedRunRef = React.useRef(null) const [addingToEvals, setAddingToEvals] = React.useState(false) const [inEvalSet, setInEvalSet] = React.useState(false) const [flagging, setFlagging] = React.useState(false) const [rerunning, setRerunning] = React.useState(false) const { runMutation, retryLastMutation } = useGuardedMutation<{ retryLastMutation: () => Promise }>({ contextId: 'agent_orchestrator.traces', blockedMessage: t('agent_orchestrator.proposal.flash.blocked'), }) const addToEvals = React.useCallback(async () => { setAddingToEvals(true) try { let created = true await runMutation({ operation: async () => { const call = await apiCallOrThrow<{ evalCase: { id: string; status: string }; created: boolean }>( `/api/agent_orchestrator/runs/${encodeURIComponent(runId)}/eval-case`, { method: 'POST' }, ) created = call.result?.created ?? true }, context: { retryLastMutation }, }) setInEvalSet(true) flash( created ? t('agent_orchestrator.traces.detail.actionAddEvalDone') : t('agent_orchestrator.traces.detail.actionAddEvalExists'), 'success', ) } catch (err) { flash(err instanceof Error ? err.message : t('agent_orchestrator.traces.detail.actionAddEvalError'), 'error') } finally { setAddingToEvals(false) } }, [runMutation, retryLastMutation, runId, t]) const toggleFlag = React.useCallback(async () => { setFlagging(true) try { let flagState: { flagged: boolean; flaggedAt: string | null } | null = null await runMutation({ operation: async () => { const call = await apiCallOrThrow<{ flagged: boolean; flaggedAt: string | null }>( `/api/agent_orchestrator/runs/${encodeURIComponent(runId)}/flag`, { method: 'POST' }, ) flagState = call.result ?? null }, context: { retryLastMutation }, }) if (flagState) { const next = flagState as { flagged: boolean; flaggedAt: string | null } setDetail((current) => current ? { ...current, run: { ...current.run, flaggedAt: next.flaggedAt } } : current, ) flash( next.flagged ? t('agent_orchestrator.traces.detail.actionFlagDone') : t('agent_orchestrator.traces.detail.actionUnflagDone'), 'success', ) } } catch (err) { flash(err instanceof Error ? err.message : t('agent_orchestrator.traces.detail.actionFlagError'), 'error') } finally { setFlagging(false) } }, [runMutation, retryLastMutation, runId, t]) const rerunAgent = React.useCallback(async () => { setRerunning(true) try { let newRunId: string | null = null await runMutation({ operation: async () => { const call = await apiCallOrThrow<{ runId: string | null }>( `/api/agent_orchestrator/runs/${encodeURIComponent(runId)}/rerun`, { method: 'POST' }, ) newRunId = call.result?.runId ?? null }, context: { retryLastMutation }, }) flash(t('agent_orchestrator.traces.detail.actionRerunDone'), 'success') if (newRunId) router.push(`/backend/traces/${encodeURIComponent(newRunId)}`) } catch (err) { flash(err instanceof Error ? err.message : t('agent_orchestrator.traces.detail.actionRerunError'), 'error') } finally { setRerunning(false) } }, [runMutation, retryLastMutation, runId, router, t]) React.useEffect(() => { let cancelled = false async function load() { // Live-refresh reloads (same run already rendered) refetch silently — // no loading flash while the operator is reading the trace. const silent = loadedRunRef.current === runId if (!silent) setIsLoading(true) setError(null) const call = await apiCall>( `/api/agent_orchestrator/runs/${encodeURIComponent(runId)}`, undefined, { fallback: {} }, ) if (cancelled) return if (!call.ok) { setError(t('agent_orchestrator.traces.detail.error')) setIsLoading(false) return } loadedRunRef.current = runId setDetail(mapRunDetail(call.result ?? {})) setIsLoading(false) } if (runId) void load() return () => { cancelled = true } }, [t, runId, reloadToken]) // Live refresh: a "Running" trace updates when its run completes or its // trace ingests (broadcast payloads carry the run id — filter client-side). const coalescedDetailReload = useCoalescedReload( React.useCallback(() => setReloadToken((token) => token + 1), []), ) useAppEvent( 'agent_orchestrator.run.completed', (event) => { if (event.payload?.id === runId) coalescedDetailReload() }, [runId], ) useAppEvent( 'agent_orchestrator.run.ingested', (event) => { if (event.payload?.id === runId) coalescedDetailReload() }, [runId], ) const timeline = React.useMemo( () => (detail ? buildSpanTimeline(detail.spans, detail.run.latencyMs) : null), [detail], ) return (
{isLoading ? ( ) : error ? ( ) : !detail ? ( ) : ( (() => { const run = detail.run // A run whose input reproduced an approved golden case is scored twice — // once online (against its own output) and once against the golden case's // `expected` (tagged `matchedEvalCaseId`). Dedupe by assertion key, // preferring the online verdict, so each assertion shows once; the golden // match is surfaced as its own header badge, not a duplicate list. const dedupedEvalResults = (() => { const seen = new Set() const out: typeof detail.evalResults = [] for (const result of [...detail.evalResults].sort( (a, b) => (a.matchedEvalCaseId == null ? 0 : 1) - (b.matchedEvalCaseId == null ? 0 : 1), )) { if (seen.has(result.assertionKey)) continue seen.add(result.assertionKey) out.push(result) } return out })() // Skipped results (passed === null) are excluded from BOTH sides of the // ratio: counting them in the denominator would report "3 of 5 passed" // for a run where two assertions never applied. const evalApplied = dedupedEvalResults.filter((result) => result.passed !== null) const evalTotal = evalApplied.length const evalPass = evalApplied.filter((result) => result.passed === true).length const evalSkipped = dedupedEvalResults.length - evalTotal const allPassed = evalTotal > 0 && evalPass === evalTotal const tokensTotal = run.inputTokens != null || run.outputTokens != null ? (run.inputTokens ?? 0) + (run.outputTokens ?? 0) : null const tokensLabel = formatTokens(tokensTotal) const costLabel = formatCostMinor(run.costMinor, run.currency) const gated = run.humanConfirmedAt == null && run.resultKind === 'proposal' const runLabel = run.externalRunId ?? `RUN-${run.id.slice(0, 8)}` const subtitle = [ run.agentVersion ? `v${run.agentVersion}` : null, run.model, run.runtime, ].filter(Boolean).join(' — ') const axisTicks = timeline ? [0, 0.25, 0.5, 0.75, 1].map( (fraction) => formatDurationMs(timeline.timeAtRatio(fraction) - timeline.startMs) ?? '', ) : [] return (
{/* Header + run stats — one card */}
{runLabel} {t('agent_orchestrator.traces.detail.spansCount', undefined, { count: detail.spans.length })} {t(runStatusLabelKey(run.status))}
{evalTotal > 0 ? ( {t('agent_orchestrator.traces.detail.evalsBadge', undefined, { passed: evalPass, total: evalTotal, })} ) : null} {run.goldenCaseId ? ( {t('agent_orchestrator.traces.detail.goldenMatch', 'Golden match')} ) : null} {gated ? ( {t('agent_orchestrator.traces.detail.gated')} ) : null}

{run.agentId}

{subtitle ?

{subtitle}

: null}
{detail.proposals.length === 1 ? ( ) : detail.proposals.length > 1 ? (
{[...detail.proposals] .sort((a, b) => (a.createdAt ?? '').localeCompare(b.createdAt ?? '')) .map((proposal) => ( ))}
) : null} {run.workflowInstanceId ? ( ) : null} {inEvalSet ? ( ) : ( )}
{run.errorMessage ? (
{t('agent_orchestrator.traces.detail.runError')}:{' '} {run.errorMessage}
) : null}
{/* Run stats — same card, hairline-divided spec grid */}
{/* Tokens and cost are runtime-reported; the opencode runtime supplies neither, and a permanent '—' tile reads as a bug rather than as an absent metric. Timing tiles keep their dash — those are expected to fill in. */} {tokensLabel ? ( ) : null} {costLabel ? ( ) : null}
{/* Execution timeline + Evaluation results — two columns */}
{detail.spans.length === 0 || !timeline ? (

{t('agent_orchestrator.traces.detail.noSpans')}

) : (
{detail.spans.map((span) => { // Geometry comes from the piecewise model, never from // duration/total — see lib/trace/spanTimeline.ts. The // duration on the right is always the TRUE one. const bar = timeline.bars.find((candidate) => candidate.id === span.id) if (!bar) return null const leftPct = bar.offsetRatio * 100 const widthPct = Math.max(0.75, Math.min(100 - leftPct, bar.widthRatio * 100)) return (
{truncateSpanName(span.name)}
{timeline.gaps.map((gap, index) => (
) })} {/* Compressing a wait without saying so would trade one dishonest picture for another. */} {timeline.compressedMs > 0 ? (

{t('agent_orchestrator.traces.detail.timelineCompressed', undefined, { duration: formatDurationMs(timeline.compressedMs) ?? '', })}

) : null}
{axisTicks.map((tick, index) => ( {tick} ))}
{/* Span-kind legend — teaches the bar colors without changing them. */}
{t('agent_orchestrator.traces.detail.legend.llm')} {t('agent_orchestrator.traces.detail.legend.tool')}
)} {/* Eval assertions — the trust verdict */} {dedupedEvalResults.length === 0 ? (

{t('agent_orchestrator.traces.detail.noEvalResults')}

) : (
{evalTotal === 0 ? ( // Every assertion skipped — neither a pass nor a fail.
{t('agent_orchestrator.traces.detail.evalAllSkipped', undefined, { skipped: evalSkipped, })}
) : (
{evalPass}/{evalTotal} {allPassed ? t('agent_orchestrator.traces.detail.evalAllPassed') : t('agent_orchestrator.traces.detail.evalSomeFailed', undefined, { failed: evalTotal - evalPass, total: evalTotal, })} {evalSkipped > 0 ? ( {t('agent_orchestrator.traces.detail.evalSkippedCount', undefined, { skipped: evalSkipped, })} ) : null}
)}
    {dedupedEvalResults.map((result) => (
  • {result.passed === null ? ( ) : result.passed ? ( ) : ( )} {result.assertionKey}
  • ))}
)}
{/* Tool calls + Output — two columns */}
{run.output != null ? (
) : (

{t('agent_orchestrator.traces.detail.noOutput')}

)}
{/* Run analysis: Reasoning + Guardrails + Context assembled (TDCR) — three columns */}
{detail.guardrailChecks.length > 0 ? ( ) : null} {detail.contextBundle ? : null}
{/* Absence is still stated — it just no longer costs a card each. The copy was right; the footprint was not. */} {detail.guardrailChecks.length === 0 || !detail.contextBundle ? (

{t('agent_orchestrator.traces.detail.notRecorded', undefined, { items: [ detail.guardrailChecks.length === 0 ? t('agent_orchestrator.traces.detail.guardrails') : null, !detail.contextBundle ? t('agent_orchestrator.traces.detail.context') : null, ] .filter(Boolean) .join(', '), })}

) : null} {/* Illustrative — mock sections grouped + de-emphasized at the bottom */} {isAgentPreviewUiEnabled() ? (
{t('agent_orchestrator.traces.detail.illustrative')}
) : null}
) })() )} ) }